import 'package:flutter/material.dart';

class DismissWrapper extends StatefulWidget {
  final List<Widget> children;

  final ScrollController ctl;

  const DismissWrapper({
    Key key,
    this.children,
    this.ctl,
  }) : super(key: key);

  @override
  _DismissWrapperState createState() => _DismissWrapperState();
}

class _DismissWrapperState extends State<DismissWrapper>
    with TickerProviderStateMixin {
  AnimationController animationController;

  final columnKey = GlobalKey();

  final duration = Duration(
    milliseconds: 500,
  );

  @override
  void initState() {
    super.initState();
    animationController = AnimationController.unbounded(
      vsync: this,
      duration: duration,
      value: 0,
    );
  }

  @override
  void dispose() {
    animationController.dispose();
    super.dispose();
  }

  @override
  Widget build(BuildContext context) {
    return NotificationListener<ScrollNotification>(
      onNotification: _onCatchNotification,
      child: NotificationListener<OverscrollNotification>(
        onNotification: _onOver,
        child: Listener(
          onPointerDown: _onDown,
          onPointerMove: _onMove,
          onPointerUp: _onUp,
          child: AnimatedBuilder(
            animation: animationController,
            builder: (c, child) {
              return Transform.translate(
                child: child,
                offset: Offset(0, animationController.value),
              );
            },
            child: Column(
              key: columnKey,
              mainAxisSize: MainAxisSize.min,
              children: widget.children,
            ),
          ),
        ),
      ),
    );
  }

  Offset downPosition;
  double columnHeight;

  void _onDown(PointerDownEvent event) {
    downPosition = event.localPosition;
    RenderBox box = columnKey.currentContext.findRenderObject();
    columnHeight = box.size.height;
  }

  void _onMove(PointerMoveEvent event) {
    var y = (event.localPosition - downPosition).dy;
    if (y < 0) {
      y = 0;
    }
    if (!isScrollOver && animationController.value <= 0) {
      return;
    }
    animationController.value = y;
  }

  void _onUp(PointerUpEvent event) {
    var percent = animationController.value / columnHeight;
    final dismiss = percent > 0.5;

    if (!dismiss) {
      animationController.animateTo(0, duration: duration * percent);
    } else {
      animationController
          .animateTo(
        columnHeight,
        duration: duration * (1 - percent),
      )
          .then((v) {
        Navigator.pop(context);
      });
    }
  }

  bool _isScrollOver = true;

  bool get isScrollOver => _isScrollOver;

  set isScrollOver(bool isScrollOver) {
    _isScrollOver = isScrollOver;
  }

  bool _onCatchNotification(ScrollNotification notification) {
    isScrollOver = notification.metrics.pixels <= 0;

    if (animationController.value > 0 &&
        notification is ScrollUpdateNotification &&
        notification.scrollDelta > 0) {
      widget.ctl.jumpTo(widget.ctl.offset - notification.scrollDelta);
    }
    return true;
  }

  bool _onOver(OverscrollNotification notification) {
    isScrollOver = true;
    return true;
  }
}
